/*
Script: dks_autocompleter_extension.js
	Mootools Autocompleter plugin <http://digitarald.de/project/autocompleter/> extension to enable autocomplete of multiple items. 

License:
	MIT license.

Copyright:
	copyright (c) 2007 Philip Allard, <http://www.bossanoza.net/>.

Credits:
	- Mootools Autocompleter plugin by digitarald <http://digitarald.de/project/autocompleter/>.
	
Note:
	Autocompleter.MultiSelectable requires Mootools <http://www.mootools.net>.
	Autocompleter.MultiSelectable requires Autocompleter <http://digitarald.de/project/autocompleter/>.
	Autocompleter.MultiSelectable requires Observer <http://digitarald.de/project/autocompleter/>.
*/

Autocompleter.MultiSelectable = {};

/*
Class: Autocompleter.MultiSelectable.Base
	Base class (extends Autocompleter.Base) for multiselect enabled autocompleter.
	
Arguments:
	el - required, element that holds the autocompleter (a textbox).
	options - optional, see options below.

Options:
	multiSelect - boolean, enable/disable multiselect. defaults to true.
	wrapSelectionsWithSpacesInQuotes - boolean, enable/disable automatic quoting of selections that contain spaces. defaults to true.	
*/
Autocompleter.MultiSelectable.Base = Autocompleter.Base.extend({
  options: {    
    multiSelect: true,
    wrapSelectionsWithSpacesInQuotes: true
  },

  choiceSelect: function(el) {
    //when multiSelect is enabled, append to field value instead of overwriting it. 
    this.observer.value = this.element.value = this.options.multiSelect? this.element.value.trimLastElement() + el.inputValue + " ":el.inputValue;

		this.hideChoices();
		this.fireEvent('onSelect', [this.element], 20);
	},
     
  prefetch: function() {    
    //when multiSelect is enabled, min len test on last query element so that min len is tested for every elements.
		var elValueToTest = this.options.multiSelect? this.element.value.lastElement(): this.element.value;

    if (elValueToTest.length < this.options.minLength) this.hideChoices();
		else if (elValueToTest == this.queryValue) this.showChoices();
		else this.query();
	},

  onCommand: function(e, mouse) {
		if (mouse && this.focussed) this.prefetch();
		if (e.key && !e.shift) switch (e.key) {
			case 'enter':
				if (this.selected && this.visible) {
					this.choiceSelect(this.selected);
					e.stop();
				} return;
			case 'up': case 'down':
        //when in multiselect mode, test on last element of element (observer) value or it will not
        //listen to key up and down.
        var elValueToTest = this.options.multiSelect? this.observer.value.lastElement(): this.observer.value;
        if (elValueToTest != (this.value || this.queryValue)) this.prefetch();
				else if (this.queryValue === null) break;
				else if (!this.visible) this.showChoices();
				else {
					this.choiceOver((e.key == 'up')
						? this.selected.getPrevious() || this.choices.getLast()
						: this.selected.getNext() || this.choices.getFirst() );
					this.setSelection();
				}
				e.stop(); return;
			case 'esc': this.hideChoices(); return;
		}
		this.value = false;
	},

  updateChoices: function(choices) {
		this.choices.empty();
		this.selected = null;
		if (!choices || !choices.length) return;
		if (this.options.maxChoices < choices.length) choices.length = this.options.maxChoices;
		choices.each(this.options.injectChoice || function(choice, i){
			var el = new Element('li').setHTML(this.markQueryValue(choice));
      //wrapping in quotes if requested/needed. 
      el.inputValue = this.options.wrapSelectionsWithSpacesInQuotes? choice.wrapInQuotes(): choice;
      this.addChoiceEvents(el).injectInside(this.choices);
		}, this);
		this.showChoices();
	}
});

/*
Class: Autocompleter.MultiSelectable.Ajax.Base
	Base class for multiselect enabled XHR autocompleter.
	
Arguments:
	el - required, element that holds the autocompleter (a textbox).
	url - required, url the xhr request will be sent to.
	options - optional, see options below.

Options:
	postVar - boolean, enable/disable multiselect. defaults to true.
	postData - boolean, enable/disable automatic quoting of selections that contain spaces. defaults to true.	
	ajaxOptions -
	onRequest -
	onComplete -
*/
Autocompleter.MultiSelectable.Ajax = {};
Autocompleter.MultiSelectable.Ajax.Base = Autocompleter.MultiSelectable.Base.extend({
  // duplicated code (same as Autocompleter.Ajax.Base)
  options: {
		postVar: 'value',
		postData: {},
		ajaxOptions: {},
		onRequest: Class.empty,
		onComplete: Class.empty
	},

  // duplicated code (same as Autocompleter.Ajax.Base)
  initialize: function(el, url, options) {
		this.parent(el, options);
		this.ajax = new Ajax(url, $merge({
			autoCancel: true
		}, this.options.ajaxOptions));
		this.ajax.addEvent('onComplete', this.queryResponse.bind(this));
		this.ajax.addEvent('onFailure', this.queryResponse.bind(this, [false]));
	},

  query: function(){
    var data = $extend({}, this.options.postData);

    //query only on last element if multiSelectable is enabled. 
		data[this.options.postVar] = this.options.multiSelect? this.element.value.lastElement(): this.element.value;

		this.fireEvent('onRequest', [this.element, this.ajax]);
		this.ajax.request(data);
	},

	queryResponse: function(resp) {
    //query only on last element if multiSelectable is enabled.
		this.value = this.queryValue = this.options.multiSelect? this.element.value.lastElement(): this.element.value;

		this.selected = false;
		this.hideChoices();
		this.fireEvent(resp ? 'onComplete' : 'onFailure', [this.element, this.ajax], 20);
	}  
});

/*
Class: Autocompleter.MultiSelectable.Ajax.Json
	Ready to use extension for JSON queries.
	
Arguments:
	el - required, element that holds the autocompleter (a textbox).
	url - required, url the xhr request will be sent to.
	options - optional, see Autocompleter.MultiSelectable.Ajax options.
*/
Autocompleter.MultiSelectable.Ajax.Json = Autocompleter.MultiSelectable.Ajax.Base.extend({ 
	queryResponse: function(resp) {
		this.parent(resp);
		var choices = Json.evaluate(resp || false);
		if (!choices || !choices.length) return;
		this.updateChoices(choices);
	}
});

/*
Class: Autocompleter.MultiSelectable.Local
	ready to use extension for Local Array queries.
	
Arguments:
	el - required, element that holds the autocompleter (a textbox).
	tokens - required, list of available elements.
	options - optional, see Autocompleter.Base options.
*/
Autocompleter.MultiSelectable.Local = Autocompleter.MultiSelectable.Base.extend({
	initialize: function(el, tokens, options) {
		this.parent(el, options);
		this.tokens = tokens;
		if (this.options.filterTokens) this.filterTokens = this.options.filterTokens.bind(this);
	},

	query: function() {
		this.hideChoices();
		this.queryValue = this.element.value.lastElement();
		this.updateChoices(this.filterTokens());
	},

	filterTokens: function(token) {
		var regex = new RegExp('^' + this.queryValue.escapeRegExp(), 'i');
		return this.tokens.filter(function(token) {
			return regex.test(token);
		});
	}
});

String.extend({
	lastElement: function(separator){
		var txt = this.trim();
		var index = txt.lastIndexOf(separator || ' ');
		return (index == -1)? txt: txt.substr(index + 1, txt.length);
	},//end lastElement

	trimLastElement: function(separator){
		var txt = this.trim();
		var index = txt.lastIndexOf(separator || ' ');
		return (index == -1)? "": txt.substr(0, index + 1);
	},//end trimLastElement

	wrapInQuotes: function(){
		var index = this.trim().lastIndexOf(' ');
		return (index == -1)? this: '"' + this + '"'
	}//end wrapInQuotes
}); //end String.extend